﻿// ****************************************************************
// Copyright 2012, Stephan Burguchev
// e-mail: me@sburg.net
// ****************************************************************
// *
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml.Linq;
using Ionic.Zip;
using Ionic.Zlib;
using Updater.Common;
using Version = Updater.Utils.Version;

namespace Updater.FeedBuilder
{
    public partial class MainForm : Form
    {
        private readonly XNamespace _nsUpdate;
        private readonly NAntProperties _props;
        private readonly string _rssFeedPath;
        private readonly string _wwwRssFeedPath;
        private readonly XDocument _xdoc;

        public MainForm()
        {
            this.InitializeComponent();

            this._rssFeedPath = ConfigurationManager.AppSettings["rssFeedPath"];
            this._wwwRssFeedPath = ConfigurationManager.AppSettings["wwwRssFeedPath"];
            this._xdoc = XDocument.Load(this._rssFeedPath);
            this._nsUpdate = this._xdoc.Root.GetNamespaceOfPrefix("update");
            string[] version =
                this._xdoc.Root.Element("channel").
                     Elements("item").
                     Select(e => e.Attribute(this._nsUpdate + "version").Value).
                     ToArray();
            this.lbxUpdateItems.DataSource = version;

            string nantPropertiesPath = ConfigurationManager.AppSettings["nantPropertiesPath"];
            this._props = NAntProperties.Load(nantPropertiesPath);
            this.tbxNewVersion.Text = this._props.Properties["project.version"];

            this.cbxChecksumType.DataSource = Enum.GetNames(typeof(ChecksumType));
        }

        private void btnAdd_Click(object sender, EventArgs e)
        {
            var enclosure = new XElement("enclosure");
            enclosure.SetAttributeValue("url", this.tbxLocation.Text);
            enclosure.SetAttributeValue("length", this.numSize.Value.ToString(CultureInfo.InvariantCulture));
            enclosure.SetAttributeValue("type", this.tbxMimeType.Text);
            enclosure.SetAttributeValue(this._nsUpdate + "update-type", this.tbxUpdateType.Text);
            enclosure.SetAttributeValue(this._nsUpdate + "checksum", this.tbxChecksum.Text);
            enclosure.SetAttributeValue(this._nsUpdate + "checksum-type", this.cbxChecksumType.SelectedItem);

            var item = new XElement("item");
            item.SetAttributeValue(this._nsUpdate + "version", this.tbxVersion.Text);
            item.SetElementValue("title", this.tbxTitle.Text);
            item.SetElementValue("description", this.markupDescription(this.tbxDescription.Text));
            item.SetElementValue("pubDate", this.dtpPubDate.Value.ToString(CultureInfo.InvariantCulture));
            item.Add(enclosure);

            this._xdoc.Root.Element("channel").Add(item);

            string[] versions = this._xdoc.Root.Element("channel").Elements("item")
                                    .Select(ee => ee.Attribute(this._nsUpdate + "version").Value).ToArray();
            this.lbxUpdateItems.DataSource = versions;
        }

        private void btnBuild_Click(object sender, EventArgs e)
        {
            Version version = Version.Parse(this.tbxNewVersion.Text);
            string v = string.Format(
                "{0}.{1}.{2}.{3}{4}", version.Major, version.Minor, version.Build, version.Revision, version.Suffix);
            this._props.Properties["project.version"] = v;
            this._props.Properties["project.version.major"] = version.Major.ToString();
            this._props.Properties["project.version.minor"] = version.Minor.ToString();
            this._props.Properties["project.version.build"] = version.Build.ToString();
            this._props.Properties["project.version.revision"] = version.Revision.ToString();
            this._props.Properties["project.version.release_code"] = version.Suffix;
            this._props.Save();

            string cmd = ConfigurationManager.AppSettings["buildCommand"];
            string dir = Path.GetDirectoryName(cmd);
            var p = new Process();
            p.StartInfo.FileName = cmd;
            p.StartInfo.UseShellExecute = true;
            p.StartInfo.WorkingDirectory = dir;

            if (this.chbDebug.Checked)
            {
                p.StartInfo.Arguments = "-D:project.config=debug";
            }

            p.Start();
            p.WaitForExit();
            int exitCode = p.ExitCode;

            if (exitCode != 0)
            {
                MessageBox.Show("Build error!");
                return;
            }

            string buildPath = ConfigurationManager.AppSettings["buildPath"];
            string defaultPackagesPath = ConfigurationManager.AppSettings["defaultPackagesPath"];
            string wwwPackagesPath = ConfigurationManager.AppSettings["wwwPackagesPath"];
            string publicWwwPackagesPath = ConfigurationManager.AppSettings["publicWwwPackagesPath"];

            var dirPkg = new DirectoryInfo(defaultPackagesPath);
            var files = new Dictionary<Version, FileInfo>();
            int revision = 0;
            foreach (FileInfo file in dirPkg.EnumerateFiles())
            {
                Match m = Regex.Match(file.Name, string.Format(@"-({0}.(\d+)[a-zA-Z]?).zip$", version));
                if (!m.Success)
                {
                    continue;
                }

                Version vv = Version.Parse(m.Groups[1].Value);

                files.Add(vv, file);

                int rr = int.Parse(m.Groups[2].Value);
                if (rr > revision)
                {
                    revision = rr;
                }
            }
            //var filePkg = files.OrderByDescending(p2 => p2.Key).First();
            version = Version.Parse(version + "." + revision);

            string finalZipName = Path.Combine(wwwPackagesPath, string.Format("zip-archive-update-{0}.zip", version));
            File.Delete(finalZipName);
            try
            {
                using (var zipFile = new ZipFile(finalZipName))
                {
                    zipFile.CompressionLevel = CompressionLevel.None;
                    zipFile.AddFile(Path.Combine(buildPath, "TransportView.exe"), "");
                    zipFile.AddFile(Path.Combine(buildPath, "TransportView.exe.config"), "");
                    zipFile.Save();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                return;
            }

            if (!ZipFile.IsZipFile(finalZipName, true))
            {
                MessageBox.Show("Archive is broken!");
                return;
            }

            this.tbxChecksum.Text = Convert.ToBase64String(ChecksumHelper.ComputeFileChecksum(finalZipName, ChecksumType.Sha1));
            this.cbxChecksumType.SelectedItem = ChecksumType.Sha1.ToString();
            this.tbxMimeType.Text = "application/zip";
            this.tbxUpdateType.Text = "Updater.Updates.ZipArchiveUpdate, Updater";
            this.tbxTitle.Text = string.Format("Обновление {0}", version);
            this.numSize.Value = new FileInfo(finalZipName).Length;
            this.tbxLocation.Text = publicWwwPackagesPath + "/" + Path.GetFileName(finalZipName);
            this.tbxVersion.Text = version.ToString();
            this.dtpPubDate.Value = DateTime.Now;
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("r u sure?", "", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
            {
                return;
            }

            string version = this.lbxUpdateItems.SelectedItem.ToString();
            XElement item = this._xdoc.Root.Element("channel").Elements("item")
                                .First(el => el.Attribute(this._nsUpdate + "version").Value == version);
            item.Remove();

            string[] versions = this._xdoc.Root.Element("channel").Elements("item")
                                    .Select(ee => ee.Attribute(this._nsUpdate + "version").Value).ToArray();
            this.lbxUpdateItems.DataSource = versions;
        }

        private void btnSaveFeed_Click(object sender, EventArgs e)
        {
            this._xdoc.Save(this._rssFeedPath);
            this._xdoc.Save(this._wwwRssFeedPath);
            this.lblSaveFeed.Text = "Saved " + DateTime.Now.ToString("HH:mm");
        }

        private string markupDescription(string text)
        {
            var sb = new StringBuilder(text.Length);
            string[] lines = text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            bool isInList = false;
            foreach (string line in lines)
            {
                if (line.StartsWith("-"))
                {
                    if (!isInList)
                    {
                        sb.Append("<ul>");
                        isInList = true;
                    }

                    sb.AppendFormat("<li>{0}</li>", line.Substring(1).Trim());
                }
                else
                {
                    if (isInList)
                    {
                        isInList = false;
                        sb.Append("</ul>");
                    }

                    sb.AppendFormat("<p>{0}</p>", line);
                }
            }

            if (isInList)
            {
                sb.Append("</ul>");
            }

            return sb.ToString();
        }
    }
}
